/*!
 * @file moduleGraphic.cpp
 * @brief Ciało moduleGraphic.hh
 */

#include "engine/gfx/moduleGraphic.hh"
using namespace std;

/*--------------------------------------------------------------------------*/

moduleGraphic::moduleGraphic() :
	moduleBase("graphic"), resX(), resXf(), resY(), resYf(), isFullscreen() {

	/*
	 * Wymiary ekrany/isFullscreen
	 */
	const_cast<int&> (this->resX) = pawirr::config->getInteger("system",
			"screenX", 800);
	const_cast<float&> (this->resXf) = (float) this->resX;

	const_cast<int&> (this->resY) = pawirr::config->getInteger("system",
			"screenY", 600);
	const_cast<float&> (this->resYf) = (float) this->resY;

	const_cast<bool&> (this->isFullscreen) = (bool) pawirr::config->getInteger(
			"system", "fullscreen", 0);

	/*
	 * Inicjalizacja okna [SDL]
	 */
	this->initWindow();

	/*
	 * Inicjalizacja grafiki [Opengl]
	 */
	this->initOpengl();

	this->isPolygonMesh = &pawirr::set->put("gfx_mesh", 0)->i;
}

/*--------------------------------------------------------------------------*/

void moduleGraphic::initWindow() {

	/*
	 * Log
	 */
	stringstream ss;
	ss << LANG_GLOBAL_PREFIX_RESOLUTION << this->resX << "x" << this->resY
			<< "@" << this->isFullscreen;

	log::info(ss.str());

	/*
	 * Tworzenie okna
	 */
	SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

	SDL_WM_SetCaption(pawirr::config->getString("system", "title",
			"pawirr.Engine").c_str(), NULL);
	SDL_ShowCursor(0);

	if (!this->isFullscreen)
		this->screenSdl = SDL_SetVideoMode(this->resX, this->resY, 32,
				SDL_OPENGL | SDL_DOUBLEBUF);
	else
		this->screenSdl = SDL_SetVideoMode(this->resX, this->resY, 32,
				SDL_OPENGL | SDL_FULLSCREEN | SDL_DOUBLEBUF);

	if (this->screenSdl == NULL) {
		stringstream ss;
		ss << LANG_SDL_MAIN_PREFIX << SDL_GetError();

		log::crash(ss.str());
	}

	/*
	 * Inicjalizowanie podmodułów SDLa
	 */
	if (!IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG)) {
		stringstream ss;
		ss << LANG_SDL_IMG_PREFIX << IMG_GetError();

		log::crash(ss.str());
	} else {
		stringstream ss;
		ss << LANG_SDL_IMG_PREFIX << LANG_GLOBAL_OK;
		log::info(ss.str());
	}

	if (TTF_Init() != 0) {
		stringstream ss;
		ss << LANG_SDL_TTF_PREFIX << TTF_GetError();

		log::crash(ss.str());
	} else {
		stringstream ss;
		ss << LANG_SDL_TTF_PREFIX << LANG_GLOBAL_OK;
		log::info(ss.str());
	}

}

/*--------------------------------------------------------------------------*/

void moduleGraphic::initOpengl() {

	/*
	 * Inicjalizowanie opengl
	 */
	glewInit();

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glShadeModel(GL_SMOOTH);

	glClearDepth(1.0f);
	glCullFace(GL_BACK);
	glFrontFace(GL_CW);
	glDepthFunc(GL_LEQUAL);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

}

/*--------------------------------------------------------------------------*/

void moduleGraphic::init() {

	this->_pri = new gfxPrimitives();
	this->_fx = new gfxPosteffects();
	this->_env = new gfxEnv();
	this->_pcs = new gfxParticles();
	this->_dbg = new gfxDebug();
}

/*--------------------------------------------------------------------------*/

void moduleGraphic::flipFrame() {

	SDL_GL_SwapBuffers();

	color bl = color(0.0, 0.0, 0.0, 1.0);
	this->clearFrame(bl);
}

/*--------------------------------------------------------------------------*/

void moduleGraphic::loadingScreen(string label) {

	color bl;
	this->clearFrame(bl);

	pawirr::graphic->_pri->putRectangle(point2f(10.0f, 10.0f), point2f(
			pawirr::graphic->resX - 10.0f, 30.0), color(0.1, 0.1, 0.1, 1.0));

	pawirr::temp->fontDefault->renderToScreen(point2f(20.0, 25.0), color_white,
			label);

	glFinish();
	glFlush();

	this->flipFrame();

}

/*--------------------------------------------------------------------------*/

void moduleGraphic::clearFrame(color & cl) {
	glClearColor(cl.r, cl.g, cl.b, cl.a);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

/*--------------------------------------------------------------------------*/

void moduleGraphic::setRenderMode(_render_mode mode) {

	this->rmPrev = this->rmCurrent;
	this->rmCurrent = mode;

	if (mode == RM_3D) {

		/*Pozycjonowanie*/
		glViewport(0, 0, this->resX, this->resY);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();

		gluPerspective(this->_env->fieldOfView, (float) this->resX
				/ (float) this->resY, 1.0f, this->_env->visualRange * 1.6);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		this->_env->cameraVec.x = -sin(2 * M_PI
				* ((double) pawirr::keyboard->mousePos.x / (double) this->resX
						- 0.5));
		this->_env->cameraVec.y = sin(2 * M_PI
				* ((double) pawirr::keyboard->mousePos.y / (double) this->resY
						- 0.5));
		this->_env->cameraVec.z = cos(2 * M_PI
				* ((double) pawirr::keyboard->mousePos.x / (double) this->resX
						- 0.5));

		gluLookAt(this->_env->cameraPos.x, this->_env->cameraPos.y,
				this->_env->cameraPos.z, this->_env->cameraPos.x
						- this->_env->cameraVec.x, this->_env->cameraPos.y
						- this->_env->cameraVec.y, this->_env->cameraPos.z
						- this->_env->cameraVec.z, 0, 1, 0);

		/*Ucinanie*/
		glEnable(GL_CULL_FACE);
		glEnable(GL_DEPTH_TEST);

		/*Siatka*/
		if (*this->isPolygonMesh)
			glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

		pawirr::graphic->_env->startLights();
	}

	else if (mode == RM_2D) {

		/*Pozycjnowanie*/
		glViewport(0, 0, this->resX, this->resY);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();

		glOrtho(0, this->resX, this->resY, 0, -1, 1);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		/*Reszta*/
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_CULL_FACE);
		glDisable(GL_DEPTH_TEST);

		/*Normalne renderowanie polygonów*/
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

		pawirr::graphic->_env->stopLights();
	}

}

/*--------------------------------------------------------------------------*/

void moduleGraphic::setRenderTarget(texture *tx) {

	//ustawienie ekranu jako wyjścia
	if (tx == NULL) {
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
		glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

		glViewport(0, 0, this->resX, this->resY);
	}
	//ustawiamy jakąś teksturę jako drawable
	else {
		if (tx->framebuffer == 0 || tx->renderbuffer == 0) {
			glGenFramebuffersEXT(1, &tx->framebuffer);
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, tx->framebuffer);
			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
					GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tx->gltex, 0);

			glGenRenderbuffersEXT(1, &tx->renderbuffer);
			glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, tx->renderbuffer);
			glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA, tx->w, tx->h);

			GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

			if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
				cout << LANG_GLOBAL_PREFIX_FRAMEBUFFER_ERROR << status << endl;
		} else {
			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, tx->framebuffer);
			glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, tx->renderbuffer);
		}

		glViewport(0, 0, tx->w, tx->h);
	}

}

/*--------------------------------------------------------------------------*/

void moduleGraphic::restoreRenderMode() {

	if (this->rmCurrent != this->rmPrev) {
		this->setRenderMode(this->rmPrev);
	}
}

/*--------------------------------------------------------------------------*/
